//
//  OTAServiceApp.m
//  Security
//
//


#import "OTAServiceApp.h"

#import <TargetConditionals.h>
#import <Security/Security.h>
#import <Security/SecPolicyPriv.h>
#import <Security/SecTrustPriv.h>
#import <syslog.h>
#import <xpc/xpc.h>
#import <MobileAsset/MobileAsset.h>
#import <Security/Security.h>
#import <Security/SecCMS.h>
#import <Security/SecCmsMessage.h>
#import <Security/SecCmsDecoder.h>
#import <Security/SecCmsContentInfo.h>
#import <Security/SecCmsSignedData.h>
#import <CommonCrypto/CommonDigest.h>
#import <CommonCrypto/CommonDigestSPI.h>
#import <CommonNumerics/CommonBaseXX.h>
#import <ManagedConfiguration/MCProfileConnection.h>
#import <MobileGestalt.h>

#if !TARGET_IPHONE_SIMULATOR
#import <BackgroundTaskAgent/BackgroundTaskAgent.h>
#endif

#import <sys/types.h>
#import <pwd.h>
#import <stdlib.h>
#import <unistd.h>
#import <stdio.h>
#import <grp.h>
#import <sys/stat.h>
#import <syslog.h>

#define CFReleaseSafe(CF) { CFTypeRef _cf = (CF); if (_cf) {  CFRelease(_cf); } }
#define CFReleaseNull(CF) { CFTypeRef _cf = (CF); if (_cf) {  (CF) = NULL; CFRelease(_cf); } }

//#define VERBOSE_LOGGING 1

#if VERBOSE_LOGGING

static void OTAPKI_LOG(const char* sz, ...)
{
	va_list va;
    va_start(va, sz);

    FILE* fp = fopen("/tmp/OTAPKITool.log", "a");
    if (NULL != fp)
    {
        vfprintf(fp, sz, va);
        fclose(fp);
    }
    va_end(va);
}

#else

#define OTAPKI_LOG(sz, ...)

#endif

//#define NEW_LOCATION 1



/* ==========================================================================
	The following are a set of string constants used by this program.  
	
	kBaseAssetDirectoryPath -	This is the full path on the device that 
								will contain the Assets directory.  This
								directory was chosen because it is owned
								by securityd
								
	kkManifestFileName - 		The file name of the manifest file for the
								OTA PKI trust asset
 
    kAllowListFileName -        The file name of the asset file that contains
								hashes of the allowed leaf certificates whose
								trust store root has been removed
								
	kAssetVersionFileName - 	The file name of the plist file in the asset
								that contains the version number of this 
								OTA PKI trust asset. It is a plist that is a 
								dictionary with a single key with a single 
								key value pair that has the version number 
								of the asset.  This is used to ensure against
								anti-replay.
								
	kBlockKeyFileName - 		The file name of the asset file that contains
								blocked keys.  Any certificate with these keys
								will be marked as not being trusted.
 
    kGrayListedKeysFileName -   The file name of the asset file that contains
                                gray listed keys.  If a chain has any of these
                                keys, than the chain will still be approved but
                                an entry will be added to the details dictionary
                                noting that the key was gray listed
    
								
	kEVRootsFileName - 			The file name of the asset file that contains
								the list of EV OIDS and their corresponding
								certificates.  This file sets which certs will
								be considered to be EV.
								
	kCTLogsFileName - 			The file name of the asset file that contains
                                the list of Certificate Transparency logs and
                                their public keys.

	kCertsIndexFileName - 		The file name of the asset file that contains
								a hash table of offsets into the cert table
								file.  This is used to look up anchor certs.
								
	kCertsTableFileName - 		The file name of the asset file that contains
								all of the anchor certificates.  The 
								kCertsIndexFileName file is used to find the 
								correct offset in this file to retrieve a
								specific anchor certificate.
								
	kVersionNumberKey - 		The dictionary key for the kAssetVersionFileName
								file to get the version number
 
    kVersionDirectoryNamePrefix - 
                                The directory name prefix for all of the 
                                asset directorys
 
    kPKITrustDataAssetType -    The asset identifier of the OTA PKI asset
 
    
========================================================================== */

#if NEW_LOCATION
static const NSString* kBaseAssetDirectoryPath = @"/var/OTAPKI";
#else
static const NSString* kBaseAssetDirectoryPath = @"/var/Keychains";
#endif

static const NSString* kManifestFileName = @"manifest.data";
static const NSString* kAllowListFileName = @"Allowed.plist";
static const NSString* kAssetVersionFileName =  @"AssetVersion.plist";
static const NSString* kAppleESCertificatesName = @"AppleESCertificates.plist";
static const NSString* kBlockKeyFileName = @"Blocked.plist";
static const NSString* kGrayListedKeysFileName = @"GrayListedKeys.plist";
static const NSString* kEVRootsFileName = @"EVRoots.plist";
static const NSString* kCTLogsFileName = @"TrustedCTLogs.plist";
static const NSString* kCertsIndexFileName = @"certsIndex.data";
static const NSString* kCertsTableFileName = @"certsTable.data";
static const NSString* kVersionNumberKey = @"VersionNumber";
static const NSString* kAssetDirectoryName = @"Assets";
static const NSString* kAssetDirectoryUser = @"_securityd";
static const NSString* kAssetDirectoryGroup = @"wheel";
#if NEW_LOCATION
static const unsigned long kAssetDirectoryPermission = S_IRWXU | S_IRWXG | S_IRWXO;
#else
static const unsigned long kAssetDirectoryPermission = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
#endif
static const NSString* kVersionDirectoryNamePrefix = @"Version_";
static const NSString* kPKITrustDataAssetType =@"com.apple.MobileAsset.PKITrustServices.PKITrustData";

const char *kOTAPKIAssetToolActivity = "com.apple.OTAPKIAssetTool.asset-check";
const char *kOTAPKIAssetToolBTAJob = "com.apple.BTA.OTAPKIAssetTool.asset-check";

static const UInt8 kApplePKISettingsRootCACert[] = {
	0x30, 0x82, 0x07, 0xca, 0x30, 0x82, 0x05, 0xb2, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x08, 0x4e,
	0xa1, 0x31, 0xe7, 0xca, 0x50, 0xb8, 0x97, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
	0x0d, 0x01, 0x01, 0x0d, 0x05, 0x00, 0x30, 0x81, 0x84, 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55,
	0x04, 0x03, 0x0c, 0x2f, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x50, 0x4b, 0x49, 0x20, 0x53, 0x65,
	0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74,
	0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
	0x69, 0x74, 0x79, 0x31, 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x1d, 0x41, 0x70,
	0x70, 0x6c, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
	0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x13, 0x30, 0x11, 0x06,
	0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0a, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x63, 0x2e,
	0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x30, 0x1e, 0x17,
	0x0d, 0x31, 0x33, 0x30, 0x36, 0x32, 0x34, 0x32, 0x33, 0x33, 0x33, 0x33, 0x39, 0x5a, 0x17, 0x0d,
	0x34, 0x33, 0x30, 0x36, 0x31, 0x37, 0x32, 0x33, 0x33, 0x33, 0x33, 0x39, 0x5a, 0x30, 0x81, 0x84,
	0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2f, 0x41, 0x70, 0x70, 0x6c, 0x65,
	0x20, 0x50, 0x4b, 0x49, 0x20, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x52, 0x6f,
	0x6f, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
	0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x26, 0x30, 0x24, 0x06, 0x03,
	0x55, 0x04, 0x0b, 0x0c, 0x1d, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
	0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
	0x74, 0x79, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0a, 0x41, 0x70, 0x70,
	0x6c, 0x65, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
	0x13, 0x02, 0x55, 0x53, 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
	0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a,
	0x02, 0x82, 0x02, 0x01, 0x00, 0xce, 0x15, 0xf7, 0x6f, 0xd8, 0x42, 0x0c, 0x6f, 0x45, 0xb4, 0x04,
	0x59, 0x24, 0xcb, 0x70, 0x88, 0x84, 0x77, 0xa1, 0x91, 0x54, 0xf4, 0x87, 0x61, 0xb3, 0xd3, 0xfc,
	0xbe, 0xb6, 0x05, 0x3c, 0xb9, 0xb7, 0x7d, 0x7c, 0xbc, 0x0b, 0xe8, 0x87, 0x07, 0xcf, 0x20, 0xbe,
	0xaa, 0xeb, 0x24, 0xc5, 0xe4, 0x5c, 0xcd, 0xcb, 0x89, 0x9f, 0x7a, 0xea, 0xb4, 0x5d, 0x3b, 0x29,
	0x6c, 0xba, 0x4d, 0x15, 0xfb, 0x59, 0xd0, 0x5a, 0xea, 0x41, 0x4e, 0x0d, 0x1d, 0xf7, 0x66, 0x77,
	0xa2, 0x96, 0x56, 0xed, 0xd1, 0x16, 0x7b, 0xea, 0xf5, 0x60, 0xdf, 0x32, 0x9c, 0xa9, 0xfd, 0xbf,
	0xb8, 0x34, 0x6f, 0x57, 0x17, 0xe6, 0x04, 0x37, 0x71, 0x07, 0xc0, 0xe9, 0x0f, 0x3c, 0xed, 0x4f,
	0x31, 0x87, 0x05, 0xa4, 0xed, 0xab, 0xac, 0xd6, 0x50, 0x05, 0x5b, 0xca, 0xd3, 0xf9, 0xd6, 0xaa,
	0xaa, 0x88, 0x57, 0x66, 0xf6, 0x6d, 0x8d, 0x4b, 0x71, 0x29, 0xd4, 0x3d, 0x1d, 0xbc, 0x82, 0x6e,
	0x81, 0xe9, 0x19, 0xf5, 0xe1, 0x12, 0x9f, 0x47, 0xdb, 0x5c, 0xed, 0x88, 0xba, 0x51, 0xe7, 0x3a,
	0xa0, 0x77, 0x2d, 0xe6, 0xcc, 0xb4, 0x34, 0xdf, 0xad, 0xbd, 0x7b, 0xf8, 0xa7, 0x79, 0x51, 0x2d,
	0xe6, 0xc2, 0xee, 0xd2, 0x96, 0xfa, 0x60, 0x60, 0x32, 0x40, 0x41, 0x37, 0x12, 0xeb, 0x63, 0x99,
	0x3d, 0xf3, 0x21, 0xbe, 0xdf, 0xa1, 0x77, 0xe6, 0x81, 0xa9, 0x99, 0x0c, 0x4b, 0x43, 0x0c, 0x05,
	0x6a, 0x6b, 0x8f, 0x05, 0x02, 0xd9, 0x43, 0xab, 0x72, 0x76, 0xca, 0xa7, 0x75, 0x63, 0x85, 0xe3,
	0xa5, 0x5c, 0xc0, 0xd6, 0xd4, 0x1c, 0xeb, 0xac, 0x2c, 0x9a, 0x15, 0x6b, 0x4e, 0x99, 0x74, 0x7d,
	0xd2, 0x69, 0x9f, 0xa8, 0xf7, 0x65, 0xde, 0xeb, 0x36, 0x85, 0xd5, 0x7e, 0x4a, 0x7a, 0x8a, 0xeb,
	0x7c, 0xcd, 0x43, 0x9e, 0x05, 0xdb, 0x34, 0xc3, 0x69, 0xbd, 0xc2, 0xe7, 0xfb, 0xa0, 0x43, 0xb3,
	0xd7, 0x15, 0x28, 0x8a, 0x91, 0xce, 0xd7, 0xa7, 0xa4, 0xcc, 0xf4, 0x1b, 0x37, 0x33, 0x76, 0xc4,
	0x58, 0xb9, 0x2d, 0x89, 0xe2, 0xb6, 0x2c, 0x56, 0x10, 0x96, 0xcc, 0xa6, 0x07, 0x79, 0x11, 0x7d,
	0x26, 0xd2, 0x85, 0x22, 0x19, 0x20, 0xb7, 0xef, 0xc3, 0xd9, 0x4e, 0x18, 0xf3, 0xaa, 0x05, 0xce,
	0x87, 0x99, 0xde, 0x76, 0x90, 0x08, 0x74, 0xac, 0x61, 0x31, 0xf8, 0x51, 0xa0, 0xc9, 0x70, 0xfc,
	0xb9, 0x22, 0xfe, 0xd2, 0x0d, 0xc8, 0x49, 0x64, 0x00, 0xe4, 0xf1, 0x53, 0xfd, 0xa1, 0xe6, 0xff,
	0x8e, 0xd6, 0xde, 0x9e, 0xcc, 0x3d, 0x37, 0x3a, 0x10, 0x62, 0x59, 0xb2, 0x34, 0x8a, 0x1d, 0xf7,
	0x9e, 0xa0, 0xbb, 0xf4, 0x53, 0xd9, 0xb8, 0x18, 0x88, 0x12, 0x5c, 0x92, 0x0d, 0xc9, 0x94, 0x7f,
	0x24, 0xb9, 0x9f, 0xda, 0x07, 0xb6, 0x79, 0x77, 0x09, 0xa3, 0x29, 0x3a, 0x70, 0x63, 0x3b, 0x22,
	0x42, 0x14, 0xd0, 0xf9, 0x7b, 0x90, 0x52, 0x2b, 0x3f, 0x7f, 0xb7, 0x41, 0x20, 0x0d, 0x7e, 0x70,
	0xd7, 0x88, 0x36, 0xa2, 0xe9, 0x81, 0x77, 0xf4, 0xb0, 0x15, 0x43, 0x9c, 0x5f, 0x4d, 0x3e, 0x4f,
	0x83, 0x79, 0x06, 0x73, 0x7a, 0xe7, 0xcb, 0x79, 0x1d, 0xec, 0xa3, 0xce, 0x93, 0x5c, 0x68, 0xbf,
	0x5a, 0xe6, 0x4c, 0x23, 0x86, 0x41, 0x7f, 0xb4, 0xfc, 0xd0, 0x2c, 0x1b, 0x64, 0x39, 0x64, 0xb7,
	0xd2, 0x1d, 0xd0, 0x2d, 0x16, 0x77, 0xfe, 0x4d, 0xad, 0xf0, 0x4f, 0x38, 0xb3, 0xf9, 0x5a, 0xee,
	0x0e, 0x1d, 0xb6, 0xf9, 0x3f, 0xba, 0x77, 0x5a, 0x20, 0xd2, 0x74, 0x1a, 0x4b, 0x5a, 0xaf, 0x62,
	0xb5, 0xd3, 0xef, 0x37, 0x49, 0xfe, 0x1e, 0xcd, 0xb5, 0xba, 0xb5, 0xa6, 0x46, 0x7b, 0x38, 0x63,
	0x62, 0x3c, 0x18, 0x7d, 0x57, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x02, 0x3c, 0x30, 0x82,
	0x02, 0x38, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x35, 0x07, 0x82,
	0xfe, 0x0e, 0x8f, 0xf5, 0xa0, 0x7c, 0x2e, 0xf9, 0x65, 0x7b, 0xa8, 0x48, 0xe8, 0x8f, 0x61, 0xb6,
	0x1c, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01,
	0x01, 0xff, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x35,
	0x07, 0x82, 0xfe, 0x0e, 0x8f, 0xf5, 0xa0, 0x7c, 0x2e, 0xf9, 0x65, 0x7b, 0xa8, 0x48, 0xe8, 0x8f,
	0x61, 0xb6, 0x1c, 0x30, 0x82, 0x01, 0xd3, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x82, 0x01, 0xca,
	0x30, 0x82, 0x01, 0xc6, 0x30, 0x82, 0x01, 0xc2, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x63,
	0x64, 0x05, 0x01, 0x30, 0x82, 0x01, 0xb3, 0x30, 0x82, 0x01, 0x78, 0x06, 0x08, 0x2b, 0x06, 0x01,
	0x05, 0x05, 0x07, 0x02, 0x02, 0x30, 0x82, 0x01, 0x6a, 0x1e, 0x82, 0x01, 0x66, 0x00, 0x52, 0x00,
	0x65, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x65, 0x00, 0x20, 0x00,
	0x6f, 0x00, 0x6e, 0x00, 0x20, 0x00, 0x74, 0x00, 0x68, 0x00, 0x69, 0x00, 0x73, 0x00, 0x20, 0x00,
	0x63, 0x00, 0x65, 0x00, 0x72, 0x00, 0x74, 0x00, 0x69, 0x00, 0x66, 0x00, 0x69, 0x00, 0x63, 0x00,
	0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x62, 0x00, 0x79, 0x00, 0x20, 0x00, 0x61, 0x00,
	0x6e, 0x00, 0x79, 0x00, 0x20, 0x00, 0x70, 0x00, 0x61, 0x00, 0x72, 0x00, 0x74, 0x00, 0x79, 0x00,
	0x20, 0x00, 0x61, 0x00, 0x73, 0x00, 0x73, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x73, 0x00,
	0x20, 0x00, 0x61, 0x00, 0x63, 0x00, 0x63, 0x00, 0x65, 0x00, 0x70, 0x00, 0x74, 0x00, 0x61, 0x00,
	0x6e, 0x00, 0x63, 0x00, 0x65, 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20, 0x00, 0x74, 0x00,
	0x68, 0x00, 0x65, 0x00, 0x20, 0x00, 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x20, 0x00,
	0x61, 0x00, 0x70, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x63, 0x00, 0x61, 0x00, 0x62, 0x00,
	0x6c, 0x00, 0x65, 0x00, 0x20, 0x00, 0x73, 0x00, 0x74, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00,
	0x61, 0x00, 0x72, 0x00, 0x64, 0x00, 0x20, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6d, 0x00,
	0x73, 0x00, 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x20, 0x00, 0x63, 0x00, 0x6f, 0x00,
	0x6e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x73, 0x00,
	0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73, 0x00, 0x65, 0x00, 0x2c, 0x00,
	0x20, 0x00, 0x63, 0x00, 0x65, 0x00, 0x72, 0x00, 0x74, 0x00, 0x69, 0x00, 0x66, 0x00, 0x69, 0x00,
	0x63, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x6c, 0x00,
	0x69, 0x00, 0x63, 0x00, 0x79, 0x00, 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x20, 0x00,
	0x63, 0x00, 0x65, 0x00, 0x72, 0x00, 0x74, 0x00, 0x69, 0x00, 0x66, 0x00, 0x69, 0x00, 0x63, 0x00,
	0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x20, 0x00, 0x70, 0x00, 0x72, 0x00,
	0x61, 0x00, 0x63, 0x00, 0x74, 0x00, 0x69, 0x00, 0x63, 0x00, 0x65, 0x00, 0x20, 0x00, 0x73, 0x00,
	0x74, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00,
	0x73, 0x00, 0x2e, 0x30, 0x35, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16,
	0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x61, 0x70, 0x70, 0x6c,
	0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
	0x65, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
	0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
	0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x6f, 0x8a,
	0xb7, 0x35, 0x73, 0x5a, 0xc5, 0x34, 0xf7, 0x8c, 0xf0, 0xd1, 0x4a, 0x17, 0x52, 0x1c, 0x70, 0xf0,
	0xe0, 0x53, 0xb4, 0x16, 0xde, 0x81, 0xda, 0x2a, 0xa4, 0xf9, 0x5b, 0x0e, 0xa6, 0x17, 0x86, 0x52,
	0xc6, 0x70, 0x73, 0xf3, 0x3f, 0x1c, 0x87, 0x94, 0xdd, 0xfe, 0x02, 0x0b, 0x85, 0xc9, 0xb9, 0xcf,
	0x15, 0x91, 0x05, 0x2e, 0x7e, 0xeb, 0xe6, 0xce, 0x0e, 0x4e, 0xd1, 0xf7, 0xe2, 0xd7, 0xf4, 0x60,
	0xd2, 0xfc, 0x1d, 0xbf, 0xad, 0x61, 0x28, 0xf8, 0x53, 0x31, 0xb3, 0x92, 0xef, 0xa4, 0x05, 0x34,
	0x97, 0x57, 0x97, 0x56, 0x3b, 0x12, 0x20, 0x2d, 0x88, 0x76, 0x81, 0x0e, 0x77, 0x85, 0xf1, 0x37,
	0xc6, 0x19, 0x8b, 0x23, 0xc2, 0x42, 0x55, 0x40, 0xc9, 0x91, 0x5c, 0x78, 0xc5, 0xe6, 0x77, 0xfe,
	0x72, 0x5f, 0xb2, 0x2c, 0x00, 0xf2, 0xe6, 0x8c, 0xcc, 0x02, 0x49, 0xd9, 0x78, 0x20, 0xae, 0xbd,
	0x75, 0x61, 0x6a, 0xaa, 0xc5, 0x71, 0x3e, 0x5d, 0x02, 0xdf, 0xd2, 0x91, 0x5c, 0x0a, 0x85, 0xc9,
	0x59, 0x7d, 0x4e, 0x89, 0x21, 0x59, 0x59, 0xe3, 0xc7, 0xdc, 0xff, 0x1e, 0x62, 0x1e, 0xb9, 0x62,
	0x2c, 0x34, 0x49, 0x15, 0xd9, 0xdf, 0x47, 0x99, 0x39, 0xcc, 0x1a, 0x01, 0xc0, 0xda, 0x48, 0x44,
	0xd4, 0x8b, 0xd3, 0x17, 0x7e, 0x39, 0xf9, 0x00, 0xe1, 0x2a, 0x46, 0xaa, 0x14, 0x22, 0xa1, 0x38,
	0x09, 0x0b, 0xb7, 0x0c, 0x88, 0xa5, 0x73, 0xfd, 0xc4, 0x6b, 0xee, 0x07, 0xb4, 0x1b, 0xb3, 0x4a,
	0xab, 0xae, 0xf6, 0xe7, 0x04, 0x61, 0x4b, 0x34, 0x7a, 0xe4, 0xff, 0xf9, 0x30, 0x28, 0x61, 0x92,
	0x52, 0x58, 0x10, 0x15, 0x3a, 0x9f, 0x0a, 0xaf, 0x15, 0x29, 0x6c, 0x67, 0xc4, 0xb4, 0xcf, 0xe6,
	0xf9, 0x46, 0x68, 0xe2, 0x2a, 0x97, 0x29, 0x16, 0xed, 0x1a, 0x9b, 0x9a, 0x45, 0x70, 0x3c, 0xf2,
	0xdf, 0x29, 0x20, 0x9e, 0x33, 0x4b, 0x5b, 0x8d, 0xf6, 0x19, 0xec, 0x4b, 0xae, 0x1a, 0x2f, 0x53,
	0x03, 0x9a, 0xfd, 0x68, 0x39, 0x58, 0xf7, 0x2e, 0x07, 0x9c, 0xf1, 0x3c, 0x1b, 0x47, 0x43, 0x19,
	0x81, 0x0e, 0x0a, 0xbb, 0x84, 0xa0, 0xda, 0x87, 0xbc, 0x8a, 0x2a, 0xb7, 0x9c, 0xe1, 0xf9, 0xeb,
	0x37, 0xb0, 0x11, 0x20, 0x7e, 0x4c, 0x11, 0x2e, 0x54, 0x30, 0xce, 0xaf, 0x63, 0xed, 0x6a, 0x63,
	0x1f, 0x1e, 0x61, 0x62, 0x04, 0xf3, 0x3a, 0x5f, 0x26, 0x6c, 0x5c, 0xd7, 0xba, 0x4f, 0xf2, 0x61,
	0x26, 0x29, 0x99, 0xea, 0x61, 0x84, 0x0d, 0x68, 0xa2, 0x5d, 0x9b, 0x5c, 0xe7, 0x86, 0x1d, 0xef,
	0xf4, 0x6f, 0x3b, 0x6c, 0x67, 0xf0, 0x70, 0xe9, 0xc5, 0xdc, 0x0a, 0x9d, 0x0f, 0xdc, 0xcc, 0x0e,
	0x7b, 0xf8, 0xc4, 0xee, 0x64, 0xe4, 0xd9, 0x3f, 0x14, 0xae, 0x8f, 0xc8, 0x18, 0x4d, 0xa1, 0xe4,
	0x40, 0x2c, 0xe9, 0x13, 0xc6, 0xc1, 0xe0, 0xb9, 0x13, 0xbe, 0xd9, 0x93, 0x66, 0x56, 0x35, 0x5c,
	0xc1, 0x38, 0x7d, 0xa1, 0xbb, 0x87, 0xa5, 0x90, 0x33, 0x4f, 0xea, 0xb6, 0x37, 0x19, 0x61, 0x81,
	0x40, 0xba, 0xd7, 0x07, 0x69, 0x05, 0x15, 0x96, 0xe9, 0xde, 0x4f, 0x8a, 0x2b, 0x99, 0x5a, 0x17,
	0x3f, 0x9f, 0xcf, 0x86, 0xf5, 0x37, 0x0a, 0xa1, 0x0e, 0x25, 0x65, 0x2d, 0x52, 0xce, 0x87, 0x10,
	0x0f, 0x25, 0xc2, 0x1e, 0x0f, 0x71, 0x93, 0xb5, 0xc0, 0xb3, 0xb4, 0xd1, 0x65, 0xa8, 0xb4, 0xf6,
	0xa5, 0x71, 0xad, 0x45, 0xdb, 0xdf, 0xec, 0xe3, 0x2a, 0x7e, 0x99, 0x96, 0x5a, 0x5d, 0x69, 0xfa,
	0xdb, 0x13, 0x39, 0xb8, 0xf5, 0x58, 0xbb, 0x87, 0x69, 0x8d, 0x2c, 0x6d, 0x39, 0xff, 0x26, 0xce,
	0x2c, 0xa8, 0x5a, 0x7e, 0x4b, 0x3f, 0xed, 0xac, 0x5f, 0xf0, 0xef, 0x48, 0xd3, 0xf8      
};


static const UInt8 kAppleTestPKISettingsRootCACert[] = {
    0x30, 0x82, 0x05, 0xd7, 0x30, 0x82, 0x03, 0xbf, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x08, 0x26,
    0xfe, 0xf8, 0xda, 0x41, 0xf3, 0x61, 0x90, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
    0x0d, 0x01, 0x01, 0x0d, 0x05, 0x00, 0x30, 0x79, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04,
    0x03, 0x0c, 0x24, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x50, 0x4b, 0x49, 0x20, 0x53, 0x65, 0x74,
    0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20,
    0x54, 0x45, 0x53, 0x54, 0x49, 0x4e, 0x47, 0x31, 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b,
    0x0c, 0x1d, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
    0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31,
    0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0a, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20,
    0x49, 0x6e, 0x63, 0x2e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
    0x53, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x30, 0x34, 0x32, 0x32, 0x32, 0x30, 0x33, 0x31, 0x34,
    0x36, 0x5a, 0x17, 0x0d, 0x34, 0x33, 0x30, 0x34, 0x31, 0x35, 0x32, 0x30, 0x33, 0x31, 0x34, 0x36,
    0x5a, 0x30, 0x79, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x24, 0x41, 0x70,
    0x70, 0x6c, 0x65, 0x20, 0x50, 0x4b, 0x49, 0x20, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
    0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x54, 0x45, 0x53, 0x54, 0x49,
    0x4e, 0x47, 0x31, 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x1d, 0x41, 0x70, 0x70,
    0x6c, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
    0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
    0x55, 0x04, 0x0a, 0x0c, 0x0a, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31,
    0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x30, 0x82, 0x02, 0x22,
    0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
    0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0x84, 0xbe, 0xc2,
    0x69, 0x9b, 0xec, 0xd5, 0xde, 0x72, 0xf0, 0x4f, 0x78, 0x81, 0x10, 0xa9, 0x56, 0x59, 0x77, 0x9c,
    0x46, 0x95, 0xd7, 0xb7, 0x0b, 0x77, 0x73, 0x02, 0xce, 0xf8, 0xaa, 0x32, 0x89, 0xee, 0xbe, 0xaa,
    0x40, 0x53, 0xf9, 0x2d, 0x96, 0x08, 0xcd, 0x2a, 0xa4, 0x61, 0xd4, 0xfd, 0x7d, 0x67, 0x2a, 0x35,
    0xc1, 0xfc, 0x43, 0xa4, 0x9c, 0xd0, 0xbd, 0xcd, 0x82, 0x27, 0xed, 0xa1, 0x1c, 0x2d, 0x9a, 0x62,
    0xd5, 0x99, 0xbd, 0x74, 0xaa, 0xf3, 0xce, 0x78, 0xc6, 0x47, 0x07, 0x43, 0x04, 0x5b, 0xbc, 0x27,
    0x5e, 0x26, 0x3e, 0x77, 0x90, 0x69, 0x7a, 0xf6, 0xe0, 0x8e, 0xaa, 0xdf, 0x96, 0x12, 0x2c, 0xb2,
    0x8b, 0xb9, 0x7e, 0x17, 0xfe, 0xde, 0x99, 0x67, 0x9b, 0x50, 0x13, 0x5c, 0x8d, 0x15, 0x26, 0x0a,
    0x9f, 0x08, 0x2f, 0x3f, 0x7c, 0x01, 0x2c, 0x3e, 0xa1, 0xba, 0xb1, 0x25, 0x33, 0xe5, 0xd9, 0x39,
    0x37, 0xde, 0x06, 0x3a, 0x63, 0x48, 0xa0, 0x9d, 0x3b, 0xa5, 0x72, 0x46, 0xfb, 0x6e, 0xa2, 0xd4,
    0x74, 0xe6, 0xf1, 0xc1, 0x69, 0xc8, 0x31, 0xff, 0x58, 0x84, 0x3a, 0xc2, 0x6b, 0x9a, 0x0d, 0x19,
    0x76, 0xe4, 0xd4, 0x4d, 0x85, 0xbc, 0x84, 0xf0, 0x07, 0x75, 0x66, 0x5f, 0xd7, 0xea, 0xab, 0x9e,
    0x46, 0xf2, 0x8a, 0x29, 0xab, 0x73, 0x57, 0xaf, 0x95, 0x4f, 0xc7, 0xf3, 0x3b, 0x55, 0xb4, 0x26,
    0x57, 0x68, 0xe9, 0x5a, 0x34, 0xbb, 0xa9, 0x39, 0xb3, 0x57, 0x5f, 0x25, 0x93, 0xd6, 0x34, 0xb7,
    0xd1, 0xc4, 0xd7, 0x70, 0xed, 0x30, 0xdb, 0x21, 0xc1, 0xcc, 0xdf, 0xed, 0xec, 0x37, 0xc5, 0xdc,
    0x0b, 0xc9, 0x85, 0x46, 0x26, 0xa7, 0x51, 0xc8, 0xdd, 0xe6, 0x47, 0xfc, 0x37, 0xd6, 0x73, 0x6f,
    0x91, 0x3d, 0xef, 0xd8, 0xa4, 0xa5, 0x08, 0x32, 0x8c, 0xae, 0x8f, 0x57, 0xf7, 0x99, 0x48, 0xef,
    0x81, 0x44, 0xac, 0x80, 0x42, 0x57, 0x9f, 0x64, 0x77, 0x40, 0x2a, 0xec, 0x03, 0x21, 0x79, 0x01,
    0x0b, 0x87, 0xc3, 0x9d, 0x22, 0xc9, 0xc0, 0x69, 0xe0, 0x34, 0xff, 0x73, 0xdd, 0x1e, 0x1b, 0x0c,
    0xe0, 0x68, 0xf0, 0x8c, 0x7a, 0x4b, 0xcd, 0x1d, 0x3f, 0x38, 0x2d, 0xe8, 0x9b, 0x91, 0xa6, 0xfe,
    0xa8, 0x8b, 0x45, 0x1c, 0xdf, 0xaf, 0x49, 0x34, 0x48, 0x17, 0x02, 0x28, 0xdb, 0xe0, 0x6e, 0x74,
    0x34, 0xea, 0xac, 0x6b, 0x00, 0x45, 0x89, 0xa9, 0xb5, 0x63, 0xbd, 0x2f, 0xe0, 0x58, 0x2e, 0xd3,
    0xc2, 0x74, 0xa2, 0x37, 0x37, 0x62, 0xf6, 0x76, 0x1b, 0x3f, 0xfb, 0x98, 0x64, 0x13, 0xd6, 0x8c,
    0xa0, 0x0c, 0xbc, 0x54, 0x00, 0xe0, 0xf8, 0x63, 0x17, 0x22, 0x44, 0x36, 0xe0, 0x28, 0xa0, 0x7d,
    0x50, 0x9e, 0x50, 0x94, 0xea, 0xd7, 0x62, 0xab, 0x6d, 0x7a, 0x19, 0xa4, 0xa2, 0x74, 0x79, 0x5d,
    0x15, 0x85, 0x21, 0xfe, 0x9a, 0x35, 0x76, 0x40, 0x78, 0x01, 0xe3, 0x46, 0x2f, 0x6f, 0x2d, 0x0a,
    0x1d, 0xac, 0x2e, 0x23, 0xec, 0xb8, 0x48, 0x74, 0xbc, 0xee, 0x29, 0x72, 0xb6, 0xe7, 0x52, 0x8c,
    0xd4, 0x1a, 0x00, 0x34, 0x75, 0x1c, 0x4b, 0x83, 0x50, 0xbb, 0x57, 0x21, 0x9b, 0xd8, 0xb4, 0x75,
    0xf3, 0x98, 0x8a, 0x9b, 0x45, 0xa8, 0x61, 0x50, 0x10, 0xb4, 0xec, 0x91, 0x2e, 0xe7, 0xf2, 0xb8,
    0xb9, 0x62, 0x70, 0xc2, 0x93, 0xe7, 0xd9, 0xf1, 0x02, 0x27, 0xd7, 0xec, 0xde, 0x5b, 0x42, 0xa1,
    0x26, 0x37, 0x41, 0x32, 0x65, 0x11, 0x63, 0x38, 0xbb, 0x6f, 0x23, 0x7a, 0xa0, 0xb7, 0x24, 0xeb,
    0xa8, 0x38, 0x8b, 0xa7, 0x73, 0xe2, 0xc8, 0x30, 0x56, 0x73, 0x6f, 0x17, 0x6e, 0x1a, 0xe5, 0x32,
    0xff, 0xd6, 0xa2, 0x08, 0x7b, 0x6a, 0x23, 0x33, 0x9f, 0x10, 0x05, 0x71, 0xdd, 0x02, 0x03, 0x01,
    0x00, 0x01, 0xa3, 0x63, 0x30, 0x61, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
    0x14, 0xd2, 0xa5, 0x3b, 0xf2, 0x5d, 0xfd, 0x1f, 0x25, 0xda, 0xfb, 0x06, 0xfb, 0x59, 0x99, 0xc4,
    0xac, 0xc4, 0x0b, 0xac, 0x64, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04,
    0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
    0x16, 0x80, 0x14, 0xd2, 0xa5, 0x3b, 0xf2, 0x5d, 0xfd, 0x1f, 0x25, 0xda, 0xfb, 0x06, 0xfb, 0x59,
    0x99, 0xc4, 0xac, 0xc4, 0x0b, 0xac, 0x64, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
    0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
    0x0d, 0x01, 0x01, 0x0d, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x71, 0x10, 0x3c, 0x89, 0xd5,
    0xc0, 0x00, 0xdc, 0x36, 0x1d, 0x93, 0xaa, 0xab, 0x4a, 0xb6, 0xfa, 0xa8, 0x5b, 0x89, 0x1c, 0xb3,
    0x4a, 0x04, 0x2e, 0xb3, 0x25, 0x0f, 0x12, 0x07, 0x29, 0x70, 0x3d, 0x34, 0xd1, 0xdd, 0x7e, 0x30,
    0xfd, 0xf5, 0xfa, 0x94, 0xf4, 0xcb, 0xdb, 0xac, 0x1b, 0xed, 0xe5, 0x11, 0x4a, 0xc8, 0xab, 0x26,
    0xe2, 0x41, 0xcb, 0xa5, 0x74, 0x4b, 0xe1, 0xd2, 0xf3, 0x83, 0x1c, 0x7a, 0xcb, 0x29, 0xd9, 0xd2,
    0xa6, 0x9d, 0x08, 0x95, 0x73, 0x63, 0xe2, 0x9c, 0xeb, 0xa5, 0x82, 0x8b, 0x6c, 0xf4, 0x64, 0x98,
    0x03, 0x53, 0x91, 0x35, 0x04, 0x89, 0x25, 0xa0, 0x1f, 0xdc, 0x42, 0xf7, 0x59, 0x44, 0x63, 0x75,
    0xe6, 0x49, 0x10, 0x66, 0x0f, 0x08, 0x07, 0x39, 0xc4, 0x3e, 0x1f, 0xba, 0x30, 0x42, 0xf8, 0x7a,
    0xc8, 0xbe, 0x6f, 0xdb, 0xec, 0x16, 0xb2, 0x76, 0x84, 0x2c, 0x6e, 0x20, 0xd1, 0xbd, 0xd5, 0x90,
    0x22, 0x0a, 0x90, 0x5c, 0x70, 0x47, 0xc9, 0x2d, 0xe3, 0x77, 0x74, 0xfd, 0xbb, 0x85, 0x1a, 0xd8,
    0x5c, 0x38, 0x94, 0x4c, 0x83, 0x28, 0x23, 0xa5, 0x4f, 0x55, 0x5f, 0xe3, 0x42, 0x80, 0x10, 0xd4,
    0xa5, 0x8d, 0xcf, 0x8b, 0x53, 0x69, 0x6d, 0xc5, 0x37, 0xd2, 0xfa, 0xbb, 0xc0, 0x5a, 0xab, 0x6f,
    0x71, 0x37, 0x92, 0xd4, 0x90, 0xef, 0x5d, 0xf1, 0xc3, 0xb8, 0x64, 0x08, 0xd3, 0xba, 0x36, 0x69,
    0x2b, 0x00, 0xed, 0xad, 0x36, 0x21, 0x38, 0xdf, 0x4a, 0xc6, 0x44, 0xc4, 0x6b, 0xd8, 0xb0, 0x7f,
    0x67, 0x05, 0xaa, 0x6f, 0x9e, 0x8a, 0xf1, 0x81, 0x95, 0x99, 0xb9, 0x56, 0xf4, 0x73, 0xa7, 0xb4,
    0x19, 0xb9, 0x4b, 0xb8, 0x1d, 0x10, 0xa5, 0x88, 0x7c, 0x39, 0xa3, 0x85, 0xe7, 0xba, 0x65, 0x86,
    0xca, 0xf7, 0x0e, 0xe0, 0x0d, 0x73, 0x3f, 0xea, 0x98, 0x88, 0x58, 0x73, 0xfa, 0x68, 0x5b, 0xaa,
    0x8c, 0xfd, 0x3e, 0x22, 0x3e, 0x92, 0xc7, 0xe2, 0x77, 0x14, 0x81, 0xe6, 0xd9, 0xdc, 0xc1, 0xe9,
    0xc0, 0x06, 0x57, 0xb4, 0xca, 0xb6, 0x14, 0x15, 0x16, 0x80, 0x7e, 0xc5, 0x11, 0xa4, 0x05, 0x66,
    0xad, 0x1d, 0xa3, 0xb6, 0xab, 0x2a, 0xbe, 0xd0, 0x52, 0x4e, 0x9e, 0x84, 0x61, 0x6b, 0xf4, 0x34,
    0x23, 0x94, 0x24, 0xc6, 0xc8, 0xb0, 0x94, 0x22, 0x4c, 0x3b, 0xac, 0x85, 0xe3, 0xd4, 0xf7, 0x38,
    0xe5, 0x9a, 0x76, 0xb3, 0x1b, 0xf0, 0xbc, 0x78, 0xc6, 0x6f, 0x11, 0xb3, 0x1a, 0x5c, 0x4f, 0x07,
    0x52, 0x06, 0x92, 0x7a, 0x25, 0x86, 0x91, 0x71, 0x8a, 0xf4, 0x03, 0xce, 0x19, 0x0d, 0xfc, 0xde,
    0x8f, 0xc9, 0x4e, 0x84, 0xf1, 0x17, 0x18, 0x6f, 0x37, 0x56, 0xb9, 0x76, 0x7e, 0x8f, 0xca, 0xde,
    0xd4, 0x1b, 0x2d, 0x8d, 0xcf, 0x12, 0x9f, 0xf9, 0xb9, 0x8b, 0x82, 0x8f, 0x4d, 0xb7, 0x63, 0x26,
    0x8d, 0xda, 0x35, 0x94, 0x18, 0xf9, 0x55, 0xca, 0x39, 0x09, 0xe9, 0x62, 0xe1, 0x00, 0xd8, 0x67,
    0xed, 0x5e, 0x84, 0xc2, 0xe5, 0x8e, 0x46, 0x57, 0xa4, 0xa7, 0x17, 0x70, 0xcf, 0x6d, 0xdf, 0x43,
    0x64, 0x2b, 0x36, 0xe6, 0xf3, 0xc1, 0x4c, 0x7a, 0x7e, 0x9e, 0x47, 0xc4, 0x14, 0x82, 0xbe, 0x94,
    0x73, 0x54, 0xd0, 0x2c, 0xc2, 0x31, 0xc6, 0xd5, 0xc3, 0xd7, 0xa9, 0xef, 0x11, 0x24, 0x2f, 0xd0,
    0x5b, 0xb8, 0x6a, 0x8e, 0x3c, 0xb7, 0x4b, 0x00, 0x9b, 0xc1, 0xca, 0x00, 0x6f, 0xd4, 0x73, 0x93,
    0x2e, 0x39, 0x37, 0x2a, 0x73, 0x44, 0x9b, 0x1b, 0x05, 0x1a, 0x7c, 0x2f, 0xc9, 0x2b, 0x37, 0xf3,
    0xcd, 0x8c, 0x4e, 0xc2, 0x7a, 0x6e, 0xd9, 0xd4, 0xf1, 0x8d, 0x6d, 0x07, 0x4b, 0xb5, 0x09, 0xb9,
    0x48, 0x55, 0xac, 0xc6, 0x7e, 0xbc, 0xc6, 0x76, 0xeb, 0x5f, 0x0f               

};

static NSDictionary* VerifyMessage(CFDataRef message, SecPolicyRef policy,  CFDataRef cert_data)
{
	NSDictionary* result = nil;
    
	SecTrustRef trustRef = NULL;
	CFDataRef payload = NULL;
	CFArrayRef anchors = NULL;
	OSStatus status = noErr;
    SecCertificateRef aCertRef = NULL;
	SecTrustResultType trust_result = kSecTrustResultRecoverableTrustFailure;
	
	if (NULL == message || NULL == policy || NULL == cert_data)
	{
		goto out;
	}
    
	status = SecCMSVerifyCopyDataAndAttributes(message, NULL, policy, &trustRef, &payload, NULL);
	if (noErr != status || NULL == trustRef || NULL == payload)
	{
		goto out;
	}
            
    aCertRef = SecCertificateCreateWithData(NULL, cert_data);
    if (NULL == aCertRef)
    {
        goto out;
    }
	
	anchors = CFArrayCreate(kCFAllocatorDefault, (const void **)&aCertRef, 1, &kCFTypeArrayCallBacks);
	if (NULL == anchors)
	{
		goto out;
	}
    	
	status = SecTrustSetAnchorCertificates(trustRef,  anchors);
	if (noErr != status)
	{
		goto out;
	}
	
	status = SecTrustEvaluate(trustRef, &trust_result);
        
	if (noErr != status)
	{
		goto out;
	}    
	
	if (trust_result == kSecTrustResultUnspecified)
	{
		// Life is good and we got back the expected result.
       		
		NSData* property_list_data = CFBridgingRelease(payload);
		
		NSPropertyListFormat format;
		NSError* error = nil;
		result = [NSPropertyListSerialization propertyListWithData:property_list_data options:0 format:&format error:&error];
		if (nil != error)
		{
			result = nil;
		}
	}
	
out:
    CFReleaseSafe(aCertRef)
	CFReleaseSafe(anchors);
	return result;
}

/* ==========================================================================
	Private Methods for the OTAServiceApp class
   ========================================================================== */
@interface OTAServiceApp (PrivateMethods)
- (void)processAssets:(NSArray*)assets;
- (BOOL)checkAssetVersions:(NSString *)assetDir;
- (BOOL)validateAsset:(NSString *)assetDir;
- (BOOL)validateDirectory:(NSString *)assetDir withFiles:(NSArray *)file_names;
- (NSDictionary *)decodeManifest:(NSData *)manifest_file_data;
- (BOOL)checkFileHash:(NSString *)file_path hash:(NSData *)hash;
- (BOOL)installAssetFiles:(NSString *)assetDir;
- (NSString*)getCurrentAssetDirectory;

@end

@implementation OTAServiceApp

@synthesize file_list = _file_list;
@synthesize manifest_file_name = _manifest_file_name;
@synthesize asset_version_file_name = _asset_version_file_name;
@synthesize current_asset_version = _current_asset_version;
@synthesize next_asset_version = _next_asset_version;
@synthesize fileManager = _fileManager;
@synthesize current_asset_directory = _current_asset_directory;
@synthesize assets_directory = _assets_directory;

/* --------------------------------------------------------------------------
	OTAServiceApp  init:withArguments:
	
	Initialize a new instance of the OTAServiceApp class
   -------------------------------------------------------------------------- */
- (id)init:(int)argc withArguments:(const char**)argv
{
	if ((self = [super init]))
	{
		_fileManager = [NSFileManager defaultManager];
		
		_manifest_file_name = (NSString *)kManifestFileName;
		_asset_version_file_name = (NSString *)kAssetVersionFileName;
		_file_list = [NSArray arrayWithObjects:kBlockKeyFileName, kGrayListedKeysFileName, 
								kEVRootsFileName, kCertsIndexFileName, kCertsTableFileName,
								kManifestFileName, kAssetVersionFileName, kAppleESCertificatesName, 
								kAllowListFileName, kCTLogsFileName, nil];
								
		_current_asset_version = nil;
		_next_asset_version = nil;
		_assets_directory = nil;	
		_current_asset_directory = [self getCurrentAssetDirectory];
		
		/* Default interval is one hour */
		_asset_query_retry_interval = 60.0 * 60;
		_verbose = false;
		
		int ch;
		while ((ch = getopt(argc, (char * const *)argv, "d:v")) != -1 )
		{
			switch (ch)
			{
				case 'd':
				{
					char *endptr = NULL;
					errno = 0;
					CFTimeInterval interval = strtod(optarg, &endptr);
					if ((interval == 0 && endptr == optarg) || errno == ERANGE) {
						syslog(LOG_ERR, "invalid argument '%s', ignoring", optarg);
					} else {
						syslog(LOG_NOTICE, "Setting query retry interval to %f seconds", interval);
						_asset_query_retry_interval = (CFTimeInterval)interval;
					}
				}
					break;
				case 'v':
					syslog(LOG_NOTICE, "Enabling verbose logging");
					_verbose = true;
					break;
				default:
					break;
			}
		}
        
        struct stat info;
#if NEW_LOCATION
        if (stat([kBaseAssetDirectoryPath UTF8String], &info))
		{
			OTAPKI_LOG("OTAServiceApp.init:withArguments: stat of %s failed\n", [kBaseAssetDirectoryPath UTF8String]);
			
			if (mkdir([kBaseAssetDirectoryPath UTF8String], kAssetDirectoryPermission))
			{
				OTAPKI_LOG("OTAServiceApp.init:withArguments: mkdir of %s failed\n", [kBaseAssetDirectoryPath UTF8String]);
			}
			else
			{
				if (stat([kBaseAssetDirectoryPath UTF8String], &info))
				{
					OTAPKI_LOG("OTAServiceApp.init:withArguments: second stat of %s failed\n", [kBaseAssetDirectoryPath UTF8String]);
				}
			}
		}
#else
		stat([kBaseAssetDirectoryPath UTF8String], &info);
		
        _uid = info.st_uid;
        _gid = info.st_gid;
#endif
        
	}
   
    return self;
}

#if !TARGET_IPHONE_SIMULATOR

- (void)registerBackgroundTaskAgentJobWithDelay:(CFTimeInterval)delay
{
	/*
	 * ELEs are very important, so allow asset queries on any network type and 
	 * construct the job so that it will fire as soon as possible, unless we are
	 * scheduling a retry after a failure.
	 */
	xpc_object_t job = xpc_dictionary_create(NULL, NULL, 0);
	if (delay != 0)
	{
		xpc_dictionary_set_double(job, kBackgroundTaskAgentJobWindowStartTime, CFAbsoluteTimeGetCurrent() + delay);
	}
	xpc_dictionary_set_double(job, kBackgroundTaskAgentJobWindowEndTime, CFAbsoluteTimeGetCurrent() + BACKGROUND_TASK_AGENT_JOB_WINDOW_MAX_TIME_FROM_NOW_SEC);
	xpc_dictionary_set_bool(job, kBackgroundTaskAgentNetworkRequired, true);
	xpc_dictionary_set_bool(job, kBackgroundTaskAgentCellularAllowed, true);
	xpc_dictionary_set_bool(job, kBackgroundTaskAgentAllowedDuringRoaming, true);
	xpc_dictionary_set_bool(job, kBackgroundTaskAgentPowerOptLevel, kBackgroundTaskAgentPowerDontCare);
	
	if (_verbose)
	{
		char *desc = xpc_copy_description(job);
		syslog(LOG_NOTICE, "Adding BTA job %s", desc);
		free(desc);
	}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
	BackgroundTaskAgentAddJob(kOTAPKIAssetToolBTAJob, job);
#pragma GCC diagnostic pop
}

#endif

/* --------------------------------------------------------------------------
	OTAServiceApp checkInWithActivity
	
	Check in with the XPC activity configured in OTAPKIAssetTool's launchd
	plist.  This activity will launch OTAPKIAssetTool every three days (with
	some leeway decided by the system).  At that time, we schedule a
	BackgroundTaskAgent job to be notified immediately when the network is
	available so we can perform an asset query.
   -------------------------------------------------------------------------- */
- (void)checkInWithActivity
{
#if !TARGET_IPHONE_SIMULATOR
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
	BackgroundTaskAgentInit("com.apple.OTAPKIAssetTool", dispatch_get_main_queue(), ^(const char *job_name, xpc_object_t job)
	{
		/*
		 * We're doing real work at this point, so open a transaction so
		 * that the system (hopefully) won't kill us while we're busy
		 */
		xpc_transaction_begin();
		
		if (self->_verbose)
		{
			syslog(LOG_NOTICE, "BackgroundTaskAgent job %s fired", job_name);
		}
		
		int64_t job_status = xpc_dictionary_get_int64(job, kBackgroundTaskAgentJobStatus);
		if (job_status == kBackgroundTaskAgentJobRequestError)
		{
			syslog(LOG_ERR, "Failed to create BTA job -- malformed job?");
		}
		else if (job_status == kBackgroundTaskAgentJobSatisfied)
		{
			if (self->_verbose)
			{
				syslog(LOG_NOTICE, "BTA job %s is satisfied -- performing asset query", job_name);
			}
			bool shouldReschedule = false;
			if ([self run:&shouldReschedule] || !shouldReschedule)
			{
				if (self->_verbose)
				{
					syslog(LOG_NOTICE, "Unscheduling BTA job");
				}
				BackgroundTaskAgentRemoveJob(kOTAPKIAssetToolBTAJob);
			}
			else
			{
				syslog(LOG_NOTICE, "Asset query failed due to network error. Re-scheduling BTA job for another attempt in %f seconds.", self->_asset_query_retry_interval);
				[self registerBackgroundTaskAgentJobWithDelay:self->_asset_query_retry_interval];
			}
		}
		else if (job_status == kBackgroundTaskAgentJobUnsatisfied)
		{
			/*
			 * We will receive this if the job expires before we get to do our
			 * work.  We still want to check for new assets as soon as possible,
			 * so reschedule the job.
			 */
			if (xpc_dictionary_get_bool(job, kBackgroundTaskAgentJobExpired))
			{
				[self registerBackgroundTaskAgentJobWithDelay:0];
			}
		}
		
		xpc_transaction_end();
	});
#pragma GCC diagnostic pop
#endif
	
	xpc_activity_register(kOTAPKIAssetToolActivity, XPC_ACTIVITY_CHECK_IN, ^(xpc_activity_t activity)
	{
		xpc_activity_state_t state = xpc_activity_get_state(activity);
		
		if (self->_verbose)
		{
			xpc_object_t criteria = xpc_activity_copy_criteria(activity);
			
			if (criteria != NULL)
			{
				char *desc = xpc_copy_description(criteria);
				syslog(LOG_NOTICE, "Criteria for XPC activity %s: %s", kOTAPKIAssetToolActivity, desc);
				free(desc);
			}
			else
			{
				syslog(LOG_NOTICE, "No critera for XPC activity %s", kOTAPKIAssetToolActivity);
			}
		}
		
		if (state == XPC_ACTIVITY_STATE_CHECK_IN)
		{
			/*
			 * The activity is already configured in the launchd plist, so there
			 * is nothing to do here
			 */
			if (self->_verbose)
			{
				syslog(LOG_NOTICE, "Activity %s in check in state", kOTAPKIAssetToolActivity);
			}
		}
		else if (state == XPC_ACTIVITY_STATE_RUN)
		{
#if !TARGET_IPHONE_SIMULATOR
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
			xpc_object_t job = BackgroundTaskAgentCopyJob(kOTAPKIAssetToolBTAJob);
#pragma GCC diagnostic pop
			if (job == NULL)
			{
				syslog(LOG_NOTICE, "Activity %s in run state. Scheduling BTA job for earliest network availability.", kOTAPKIAssetToolActivity);
				[self registerBackgroundTaskAgentJobWithDelay:0];
			}
			else if (self->_verbose)
			{
				syslog(LOG_NOTICE, "Already have a BTA job registered. Ignoring activity.");
			}
#else
			/*
			 * BackgroundTaskAgent doesn't exist on the iOS simulator, so we
			 * just directly try to find and download new assets.
			 */
			xpc_transaction_begin();
			bool shouldReschedule = false;
			if (![self run:&shouldReschedule]) {
				syslog(LOG_NOTICE, "Asset query failed%s.", shouldReschedule ? " due to network issue" : "");
			}
			xpc_transaction_end();
#endif
		}
	});
}

/* --------------------------------------------------------------------------
	OTAServiceApp  run
	
	Run this program and leave.  This program will currently run every 3 days,
	with some leeway based on network availability.  That will provide the
	longest time from publisihing a change in the PKI trust setting asset and
	having that asset be consumed by a device.
	
	The program simnply ask the mobile asset daemon if there are any assets
	to be processed with the PKITrustDataAssetType.  If not this program 
	will just complete and will be re-run in 3 days.  If there is an asset to 
	process then the asset will be process and then the program will complete.
 
	Returns false if the operation failed.  On return, shouldReschedule is set
	to true if the operation failed due to a network error and the caller
	should reschedule this operation at a more opportune time.
   -------------------------------------------------------------------------- */
- (bool)run:(bool *)shouldReschedule
{
	@autoreleasepool
	{
		if (shouldReschedule != NULL) {
			*shouldReschedule = false;
		}
		
		syslog(LOG_NOTICE, "OTAPKIAssetTool running");
		if (![[MCProfileConnection sharedConnection] isOTAPKIUpdatesAllowed])
	    {
            syslog(LOG_NOTICE, "OTAPKIAssetTool: OTAPKI updates are not allowed.");
	        return false;
	    }
	
		ASAssetQuery *assetQuery = [[ASAssetQuery alloc] initWithAssetType:(NSString *)kPKITrustDataAssetType];
		if (assetQuery == nil) 
		{
            syslog(LOG_NOTICE, "OTAPKIAssetTool: Could not create the asset query.");
			return false;
		}
		
		// Get the asset synchronously
		NSError *error = nil;
		NSArray *foundAssets = [assetQuery runQueryAndReturnError:&error];
		if (nil != foundAssets) 
		{
			[self processAssets:foundAssets];
		}
        else
        {
			syslog(LOG_NOTICE, "OTAPKIAssetTool: No assets returned from query: %s", [[error description] UTF8String]);
			
			NSArray *networkErrorCodes = @[ @(ASErrorNetwork), @(ASErrorNetworkNoConnection), @(ASErrorNetworkTimedOut), @(ASErrorNetworkUnexpectedResponse) ];
			if ([[error domain] isEqualToString:ASErrorDomain] && [networkErrorCodes containsObject:@([error code])])
			{
				syslog(LOG_NOTICE, "OTAPKIAssetTool: Query failed due to network error.");
				if (shouldReschedule != NULL) {
					*shouldReschedule = true;
				}
			}
			return false;
        }
		
		return true;
	}
}

/* --------------------------------------------------------------------------
	OTAServiceApp  processAssets:
	
	If when run is called asset(s) are found they will be processed here.
   -------------------------------------------------------------------------- */  
- (void)processAssets:(NSArray*)assets
{
	if (nil == assets)
	{
		return;
	}
        
	NSError* error = nil;
    ASAsset* asset = nil;
    int asset_version = 0;

	for (asset in assets)
	{
        NSDictionary* asset_attributes = asset.attributes;
        
        NSNumber* contentVersion = [asset_attributes objectForKey:@"ContentVersion"];
		OTAPKI_LOG("In processAssets: about to check the ContentVersion\n");
        if (nil != contentVersion)
        {
            asset_version = [contentVersion intValue];
            int current_asset_version_number = (nil != _current_asset_version) ? [_current_asset_version intValue] : 0;
            
            if (asset_version <= current_asset_version_number)
            {
                syslog(LOG_NOTICE, "OTAPKIAssetTool: content version %d is too small.  Current asset version id %d",
                       asset_version, current_asset_version_number);
                
				OTAPKI_LOG("In processAssets: content version is too small: current asset version is %d\nContent version is %d\n",
					current_asset_version_number, asset_version);                
				asset = nil;
                continue;
            }
        }
        
        if (nil == asset)
		{
            syslog(LOG_NOTICE, "OTAPKIAssetTool: no suitable asset found");
			return;
		}
        

		// Check to see if the asset needs to be downloaded
		if (asset.state == ASAssetStateNotPresent)
		{
			__block dispatch_semaphore_t sem = dispatch_semaphore_create(0);

			[asset setProgressHandler:^(NSDictionary *state, NSError *anError)
			{
				if (error != nil) 
				{
					// An error occured.  Signal the semaphore to bail
					dispatch_semaphore_signal(sem);
				}
				else if ([[state objectForKey:@"Operation"] isEqualToString:(NSString *) @"OperationCompleted"]) 
				{
					// The download is complete.  Signal the semaphore
					dispatch_semaphore_signal(sem);
				}
			}];
			
            NSNumber* yesValue = [NSNumber numberWithBool:YES];
            const id keys[] = {ASDownloadOptionAllow3G, ASDownloadOptionAllow4G, ASDownloadOptionPriority, ASDownloadOptionAllowBatteryPower};
            const id values[] = {yesValue, yesValue, ASDownloadPriorityHigh, yesValue};
            
            NSDictionary* options = [NSDictionary dictionaryWithObjects:values forKeys:keys count:(sizeof (keys) / sizeof(keys[0]))];
            
  			[asset beginDownloadWithOptions:options];
			dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
		}
		
		// Check to see if the asset is now available for processing
		if ([asset state] == ASAssetStateInstalled)
		{
			// Get the asset data directory
			NSString* assetDir = [[[asset localURL] URLByAppendingPathComponent:@"PKITrustData"] path];
			if (nil != assetDir)
			{
				// validate the asset. 
				OTAPKI_LOG("In processAssets: about to validateAsset\n");
				if ([self validateAsset:assetDir])
				{
					OTAPKI_LOG("In processAssets: asset validated installing\n");
					// The asset is valid so install the files
					[self installAssetFiles:assetDir];
					
					// Signal securityd to idle-exit at it's next opportunity
					OTAPKI_LOG("In processAssets: notifying securityd\n");
					int didUpdate = 0;
					(void)SecTrustOTAPKIGetUpdatedAsset(&didUpdate);
                    syslog(LOG_NOTICE, "OTAPKIAssetTool: installed new asset %d", asset_version);
                    _current_asset_version = contentVersion;
				}
                else
                {
                    syslog(LOG_NOTICE, "OTAPKIAssetTool: Asset %d did not validate", asset_version);
                }
                
				// regaurdless if the asset is valid.  Now that it is 
				// installed, it needs to be purged to ensure that 
				// we can retrieve a new updated asset.
                [asset purgeAndReturnError:&error];
			}
            else
            {
                syslog(LOG_NOTICE, "OTAPKIAssetTool: Asset directory %s not found", [assetDir UTF8String]);
            }
		}
	}
}

/* --------------------------------------------------------------------------
	OTAServiceApp  checkAssetVersions:
	
	If when run is called asset(s) are found they will be processed here.
   -------------------------------------------------------------------------- */	
- (BOOL)checkAssetVersions:(NSString *)assetDir
{
	BOOL result = NO;
	
	OTAPKI_LOG("Entering checkAssetVersions\n");
        
	if (nil == assetDir || nil == self.current_asset_version)
	{
		OTAPKI_LOG("checkAssetVersions: parameter error\n");
		return result;
	}
	
	// first get the new version number from the downloaded asset
	NSString* next_asset_version_path = [assetDir stringByAppendingPathComponent:self.asset_version_file_name];
	if (![self.fileManager fileExistsAtPath:next_asset_version_path])
	{
		// The asset is missing the AssertVersion.plist 
		// This is an invalid asset
		OTAPKI_LOG("checkAssetVersions: could not file asseet version file %s\n", [self.asset_version_file_name UTF8String]);
		return result;
	}
	
    NSError* error = nil;
	NSInputStream* input_stream = [NSInputStream inputStreamWithFileAtPath:next_asset_version_path];
	[input_stream open];
	NSDictionary* asset_dict = [NSPropertyListSerialization propertyListWithStream:input_stream options:0 format:nil error:&error];
	[input_stream close];
	
	if (nil != error)
	{
		OTAPKI_LOG("checkAssetVersions: error reading asset version file: %s\n", [[error localizedDescription] UTF8String]);
		return result;
	}
	
	_next_asset_version = [asset_dict objectForKey:kVersionNumberKey];
	if (nil == _next_asset_version)
	{
		OTAPKI_LOG("asset_dict did not have a entry with a key of kVersionNumberKey\n");
		return result;
	}
	
	// Check the current asset version against the new asset version.  The new asset version MUST be larger than the
	// current asset version
	NSInteger current_asset_version_value = [self.current_asset_version integerValue];
	NSInteger next_asset_version_value = [self.next_asset_version integerValue];

	if (next_asset_version_value <= current_asset_version_value)
	{
		OTAPKI_LOG("heckAssetVersions: assert version too small.  current_asset_version_value = %d next_asset_version_value = %d\n",
			current_asset_version_value, next_asset_version_value);
		return result;
	}
	
	return YES;
}


/* --------------------------------------------------------------------------
	OTAServiceApp  validateAsset:
	
	Decode the manifest and verify the file hashes
   -------------------------------------------------------------------------- */	
- (BOOL)validateAsset:(NSString *)assetDir
{
	BOOL result = NO;
	
	OTAPKI_LOG("Enterning validateAsset\n");
    	
	if (![self validateDirectory:assetDir withFiles:self.file_list])
	{
		OTAPKI_LOG("validateAsset param\n");
		return result;
	}
    	
	NSString* manifest_file_path = [assetDir stringByAppendingPathComponent:self.manifest_file_name];
	NSError* error = nil;
	NSData* manifest_file_data = [NSData dataWithContentsOfFile:manifest_file_path options:0 error:&error];
	if (nil != error)
	{
		OTAPKI_LOG("validateAsset: could not read manifest file. error = %s\n", [[error localizedDescription] UTF8String]);
		return result;
	}
    	
	NSDictionary* manifest_data = [self decodeManifest:manifest_file_data];
	if (nil == manifest_data)
	{
		OTAPKI_LOG("validateAsset: decodeManifest failed!\n");
		return result;
	}
        
	NSString* full_file_path = nil;
	NSData* hash = nil;
	for (NSString* file_name in self.file_list)
	{
		if ([file_name isEqualToString:self.manifest_file_name])
		{
			continue;
		}
		
		hash = [manifest_data objectForKey:file_name];
		if (nil == hash)
		{
			OTAPKI_LOG("validateAsset: could not get hash for file %s\n", [file_name UTF8String]);
			return result;
		}
		
		full_file_path = [assetDir stringByAppendingPathComponent:file_name];
		if (![self checkFileHash:full_file_path hash:hash])
		{
			OTAPKI_LOG("validateAsset: hash for file %s does not match\n", [file_name UTF8String]);
			return result;
		}
	}
    	
	result = [self checkAssetVersions:assetDir];    
	return result;
}

/* --------------------------------------------------------------------------
	OTAServiceApp  validateDirectory:withFiles:
	
	Ensure that a given directory has the files listed in the files_names 
    parameter
   -------------------------------------------------------------------------- */	
- (BOOL)validateDirectory:(NSString *)assetDir withFiles:(NSArray *)file_names
{
	BOOL result = NO;
	OTAPKI_LOG("Enterning validateDirectory\n");	
    
	if (nil == assetDir || nil == file_names)
	{
		OTAPKI_LOG("validateDirectory param error\n");
		return result;
	}
	NSError* error = nil;
	NSArray* dir_items = [self.fileManager contentsOfDirectoryAtPath:assetDir error:&error];
	if (nil != error)
	{
		OTAPKI_LOG("validateDirectory: Error calling contentsOfDirectoryAtPath: error = %s\n", [[error localizedDescription] UTF8String]);
		return result;
	}
	
	for (NSString* file_name in file_names)
	{
		if (![dir_items containsObject:file_name])
		{
			OTAPKI_LOG("validateDirectory: missing file %s\n", [file_name UTF8String]);
			return result;
		}
	}
     
	return YES;	
}

/* --------------------------------------------------------------------------
	OTAServiceApp  decodeManifest:
	
	Ensure that the asset manifest blob has it CMS signature verified
   -------------------------------------------------------------------------- */
- (NSDictionary *)decodeManifest:(NSData *)manifest_file_data
{
	NSDictionary* result = nil;
	CFDataRef cert_data = NULL;
	CFDataRef message = NULL;
	SecPolicyRef policy = NULL;
	CFBooleanRef mgResult = NULL;
	
	OTAPKI_LOG("Enterning decodeManifest\n");
	
	if (nil == manifest_file_data)
	{
		OTAPKI_LOG("decodeManifest: parameter error\n");
		goto out;
	}
	
	message = CFBridgingRetain(manifest_file_data);
	
	policy = SecPolicyCreateOTAPKISigner();
	if (NULL == policy)
	{
		OTAPKI_LOG("decodeManifest: could not get the SecPolicyCreateOTAPKISigner policyRef\n");
		goto out;
	}
    	
	cert_data = CFDataCreate(kCFAllocatorDefault, kApplePKISettingsRootCACert, sizeof(kApplePKISettingsRootCACert));
	if (NULL == cert_data)
	{
		OTAPKI_LOG("decodeManifest: could not kApplePKISettingsRootCACert data\n");
		goto out;
	}
		
	result = VerifyMessage(message, policy, cert_data);
    
	if (NULL != result)
	{
		OTAPKI_LOG("decodeManifest: SecPolicyCreateOTAPKISigner success!\n");
		goto out;
	}
	
	OTAPKI_LOG("decodeManifest: SecPolicyCreateOTAPKISigner failed! Checking to see if this is an internal build\n");
    	
	// The first attempt did not work so check to see if this is running on an internal build.
	if (!MGIsQuestionValid(kMGQAppleInternalInstallCapability))
	{
		OTAPKI_LOG("decodeManifest: kMGQAppleInternalInstallCapability had an error\n");
		goto out;
	}	
    	
	mgResult = MGCopyAnswer(kMGQAppleInternalInstallCapability, NULL);
	
	if (NULL == mgResult || !CFEqual(mgResult, kCFBooleanTrue))
	{
		OTAPKI_LOG("decodeManifest: Not an internal build");
		goto out;
	}
	
	OTAPKI_LOG("decodeManifest: This is an internal build\n");
    	
	CFReleaseNull(policy);
	CFReleaseNull(cert_data);
	
	policy = SecPolicyCreateTestOTAPKISigner();
	if (NULL == policy)
	{
		OTAPKI_LOG("decodeManifest: could not SecPolicyCreateTestOTAPKISigner policyRef\n");
		goto out;
	}
    	
	cert_data = CFDataCreate(kCFAllocatorDefault, kAppleTestPKISettingsRootCACert, sizeof(kAppleTestPKISettingsRootCACert));
	if (NULL == cert_data)
	{
		OTAPKI_LOG("decodeManifest: could not kAppleTestPKISettingsRootCACert data\n");
		goto out;
	}
    	
	result = VerifyMessage(message, policy, cert_data);
    
out:

    CFReleaseSafe(mgResult);
    CFReleaseSafe(message);
	CFReleaseSafe(policy);
	CFReleaseSafe(cert_data);
	return result;
}

/* --------------------------------------------------------------------------
	OTAServiceApp  checkFileHash:hash:
	
	Ensure that the given asset file's hash is the same as in the manifest
   -------------------------------------------------------------------------- */
- (BOOL)checkFileHash:(NSString *)file_path hash:(NSData *)hash
{
	BOOL result = NO;
	if (nil == file_path || nil == hash)
	{
		return result;
	}

	NSError* error = nil;
	NSData* file_data = [NSData dataWithContentsOfFile:file_path options:0 error:&error];
	if (nil != error)
	{
		return result;
	}

	NSMutableData *digest = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
	uint8_t *dp = (digest) ? [digest mutableBytes] : NULL;
	if (NULL == dp)
	{
		return result;
	}

	memset(dp, 0, CC_SHA256_DIGEST_LENGTH);
	CCDigest(kCCDigestSHA256,
	         (const uint8_t *)[file_data bytes],
	         (size_t)[file_data length], dp);
    
	result = [hash isEqualToData:digest];
       
	return result;
}

/* --------------------------------------------------------------------------
	OTAServiceApp  installAssetFiles:
	
	Copy over the files into the /var/Keychains/Assets directory.  
   -------------------------------------------------------------------------- */
- (BOOL)installAssetFiles:(NSString *)assetDir
{
	BOOL result = NO;
	
	OTAPKI_LOG("Entering installAssetFiles\n");
    
	if (nil == assetDir)
	{
		OTAPKI_LOG("installAssetFiles: parameter error\n");
		return result;
	}
	
	if (nil == self.assets_directory)
	{
		OTAPKI_LOG("installAssetFiles: no assets directory\n");
		return result;
	}
	
	// Create a temp directory to hold the new asset files.
	NSString* tempDir = [self.assets_directory stringByAppendingPathComponent:@"TempAssetDir"];
	NSError* error = nil;
    
#if NEW_LOCATION
	id values[] = {[NSNumber numberWithUnsignedLong: kAssetDirectoryPermission]};
    id keys[] = {NSFilePosixPermissions};
    
	NSDictionary* attributes = [NSDictionary dictionaryWithObjects:values forKeys:keys count:1];
#else
    struct passwd *user_info = getpwnam([kAssetDirectoryUser UTF8String]);
    struct group *group_info = getgrnam([kAssetDirectoryGroup UTF8String]);
    NSNumber* uid_num = [NSNumber numberWithUnsignedInt: user_info->pw_uid];
    NSNumber* gid_num = [NSNumber numberWithUnsignedInt: group_info->gr_gid];
    
    id values[] = {uid_num, gid_num, [NSNumber numberWithUnsignedLong: kAssetDirectoryPermission]};
    id keys[] = {NSFileOwnerAccountID, NSFileGroupOwnerAccountID, NSFilePosixPermissions};
    
    NSDictionary* attributes = [NSDictionary dictionaryWithObjects:values forKeys:keys count:3];
#endif
    
    
	if (![self.fileManager createDirectoryAtPath:tempDir withIntermediateDirectories:YES
                                attributes:attributes error:&error])
	{
		OTAPKI_LOG("installAssetFiles: could not create directory %s\n", [tempDir UTF8String]);
		return result;
	}
    
#ifndef NEW_LOCATION
    // Copy all of the asset files to the newly created directory
	for (NSString* file_name in self.file_list)
	{
		NSString* download_assert_path = [assetDir stringByAppendingPathComponent:file_name];
		NSString* asset_path = [tempDir stringByAppendingPathComponent:file_name];
		if ([self.fileManager copyItemAtPath:download_assert_path toPath:asset_path error:&error])
		{
             chown([asset_path UTF8String], self.uid, self.gid);
        }
        else
        {
            [self.fileManager removeItemAtPath:tempDir error:nil];
			return result;
        }
	}
#endif // !NEW_LOCATION

	
	// Now that all of the files have been copied to the temp directory make a single call
	// to rename (move) the temp directory to be the correct version directory.  This rename
	// allow for reducing a race conditions between this asset code and securityd.  
	NSInteger new_version_value = [self.next_asset_version integerValue];
	NSString* new_version_dir_name = [NSString stringWithFormat:@"%@%ld", kVersionDirectoryNamePrefix, (long)new_version_value];
	NSString* new_version_dir_path = [self.assets_directory stringByAppendingPathComponent:new_version_dir_name];
	if (![self.fileManager moveItemAtPath:tempDir toPath:new_version_dir_path error:&error])
  	{
		OTAPKI_LOG("installAssetFiles: could not move path %s\n", [tempDir UTF8String]);
		[self.fileManager removeItemAtPath:tempDir error:nil];
		return result;
	}
    
	result = YES;
	return result;
}

/* --------------------------------------------------------------------------
	OTAServiceApp  getCurrentAssetDirectory:
	
	Looks through the /var/Keychains/Assets directory to find latest asset
    version directory.  If no assets have been downloaded then nil is returned
    and the current asset version is set to 0
   -------------------------------------------------------------------------- */
- (NSString*)getCurrentAssetDirectory
{
	NSString* result = nil;
	BOOL isDir = NO;
	
	OTAPKI_LOG("In getCurrentAssetDirectory\n");
	OTAPKI_LOG("getCurrentAssetDirectory: checking to see if %s exists\n", [kBaseAssetDirectoryPath UTF8String]);
	
	// Check to see if the base directory is there
	if (![self.fileManager fileExistsAtPath:(NSString *)kBaseAssetDirectoryPath isDirectory:&isDir] || !isDir)
	{
		OTAPKI_LOG("getCurrentAssetDirectory:  %s does not exists\n", [kBaseAssetDirectoryPath UTF8String]);
		// This might be fatal
		return result;
	}
	
	NSError* error = nil;
	NSInteger version_number = 0;
	NSInteger current_version_number = 0;
	int aVerNum = 0;
	OSStatus err = noErr;
	
	_assets_directory = [kBaseAssetDirectoryPath stringByAppendingPathComponent:(NSString *)kAssetDirectoryName];
	
	OTAPKI_LOG("getCurrentAssetDirectory: %s does exists\n", [self.assets_directory  UTF8String]);
	
	if ([self.fileManager fileExistsAtPath:self.assets_directory isDirectory:&isDir] && isDir)
	{
		OTAPKI_LOG("getCurrentAssetDirectory: %s does exists\n", [self.assets_directory  UTF8String]);
		NSDirectoryEnumerator* dirEnum = [self.fileManager enumeratorAtPath:self.assets_directory];
		[dirEnum skipDescendents];
		
		for (NSString* file in dirEnum)
		{
			if ([file hasPrefix:(NSString *)kVersionDirectoryNamePrefix])
			{
				NSString* version_str = [file substringFromIndex:[kVersionDirectoryNamePrefix length]];
                NSInteger aVersion_number = [version_str integerValue];
                if (aVersion_number > version_number)
                {
                    version_number = aVersion_number;
                }
			}
		}		
	}
	else
	{ 
#if NEW_LOCATION 
 		id values[] = {[NSNumber numberWithUnsignedLong: kAssetDirectoryPermission]};
        id keys[] = {NSFilePosixPermissions};
        
        NSDictionary* attributes = [NSDictionary dictionaryWithObjects:values forKeys:keys count:1];
#else
     
        struct passwd *user_info = getpwnam([kAssetDirectoryUser UTF8String]);
        struct group *group_info = getgrnam([kAssetDirectoryGroup UTF8String]);     
        NSNumber* uid_num = [NSNumber numberWithUnsignedInt: user_info->pw_uid];
        NSNumber* gid_num = [NSNumber numberWithUnsignedInt: group_info->gr_gid];

        id values[] = {uid_num, gid_num, [NSNumber numberWithUnsignedLong: kAssetDirectoryPermission]};
        id keys[] = {NSFileOwnerAccountID, NSFileGroupOwnerAccountID, NSFilePosixPermissions};
        NSDictionary* attributes = [NSDictionary dictionaryWithObjects:values forKeys:keys count:3];
#endif
		OTAPKI_LOG("getCurrentAssetDirectory: %s does NOT exists\n", [self.assets_directory  UTF8String]);		
		OTAPKI_LOG("getCurrentAssetDirectory: creating %s\n", [self.assets_directory  UTF8String]);
		
		if (![self.fileManager createDirectoryAtPath:self.assets_directory withIntermediateDirectories:YES
                    attributes:attributes error:&error])
		{
			OTAPKI_LOG("getCurrentAssetDirectory: failed to create %s\n", [self.assets_directory  UTF8String]);
			return result;
		}
        
	}
			
	err = SecTrustGetOTAPKIAssetVersionNumber(&aVerNum);
	if (errSecSuccess == err && aVerNum > 0)
	{
		current_version_number = aVerNum;
	}
	
	if (version_number < current_version_number)
	{
		OTAPKI_LOG("The largest OTA version number is smaller than the current version number.  This means the system has a newer asset\n");
		version_number = current_version_number;
	}
	else
	{
		OTAPKI_LOG("The largest OTA version number is equal to the current version number.  The OTA asset is newer than the system asset\n");
		NSString* version_dir_name = [NSString stringWithFormat:@"%@%ld", kVersionDirectoryNamePrefix, (long)version_number];
		result = [self.assets_directory stringByAppendingPathComponent:version_dir_name];
	}
	
	OTAPKI_LOG("getCurrentAssetDirectory: setting version number to %d\n", version_number);
	_current_asset_version = [NSNumber numberWithInteger:version_number];
	return result;
}

@end
